import tkinter as tk
import random

# 绘制简单的X轴坐标
def draw_line():
    # 绘制简单的X轴坐标
    for w_x in range(m, w, m):
        canvas.create_text(w_x, h+gap, text=str(w_x - m), anchor="s")
    # 绘制简单的Y轴坐标
    for h_y in range(h, 0, -m):
        if h - h_y != 0:
            canvas.create_text(m - 2, h_y, text=str(h - h_y), anchor="e")
    # 绘制坐标轴边框
    canvas.create_rectangle(m, m, w, h)
    
# 线性函数
def line(x):
    return x * 0.5 + 10

# 随机生成样本数据
def set_data():
    global data, xy_type
    # 初始化数据
    data = []
    xy_type = []
    for _ in range(size):
        # 随机数据：350×350以内
        x = random.random() * (w-m)
        y = random.random() * (h-m)
        data.append([x, y])
        # 返回标签数据
        f_y = line(x)
        if y > f_y:
            xy_type.append(1)
        else:
            xy_type.append(0)
    # print(xy_type)
    # print(data)

# 激活函数
def activate(x_i):
    sum = 0
    for i in range(len(x_i)):
        # 权重与输入的点乘
        sum += x_i[i] * weight[i]
    if sum > 0:
        return 1
    else:
        return 0

# 反向传播
def bp(x_i, ok):
    # 防止为零时无法使用
    x_i.append(1)
    # 激活函数返回值
    res = activate(x_i)
    # 误差
    err = ok - res
    if err!= 0:
        for ii in range(len(x_i)):
            # 权重更新
            weight[ii] += x_i[ii] * err * step

# 机器学习
def ml():
    set_data()
    # 训练样本数量
    train_len = int((100 - test_size) / 100 * len(xy_type))
    for i in range(study):
        for key in range(train_len):
            inputs = data[key]
            bp(inputs, xy_type[key])
        break

# 测试数据
def test():
    if len(weight)!= 3:
        msg.config(text='请先进行训练！')
        return
    # 获取测试数据
    test_len = int((100 - test_size) / 100 * len(xy_type))
    test_arr = data[test_len:]
    # 绘制线性函数
    for x in range(w - m):
        y = line(x)
        canvas.create_rectangle(x + m, h - y, x + m + 1, h - y + 1, fill='blue')
    # 绘制测试数据
    for i in range(len(test_arr)):
        x, y = test_arr[i]
        res = activate([x, y, bias])
        if res!= 0:
            # 红色圆圈
            canvas.create_oval(x + m - 2, h - y - 2, x + m + 2, h - y + 2, fill='red')
        else:
            # 黑色矩形
            canvas.create_rectangle(x + m - 2, h - y - 2, x +m + 2, h - y + 2, fill='black')



# 创建主窗口
root = tk.Tk()
root.title("反向传播（感知器）")
# 初始化坐标图数据
w = 400
h = 400
m = 50
gap = 20
#  初始化机器学习数据
size = 1000
test_size = 20
step = 0.0001
bias = 1
study = 10000
data = []
xy_type = []
# 权重 归一化，初始化为-1到1之间的随机数
weight = [random.uniform(-1, 1) for _ in range(3)]
# 提示符
msg = tk.Label(root, text="请先点击生成样本")
msg.pack()

format = tk.Frame(root)
format.pack()
tk.Button(format, text="生成样本", command=set_data).pack(side=tk.LEFT)
tk.Label(format, text="样本数量:").pack(side=tk.LEFT)
size_entry = tk.Entry(format,width=5)
size_entry.insert(0, '1000')
size_entry.pack(side=tk.LEFT)
tk.Label(format, text="测试百分比:").pack(side=tk.LEFT)
test_size_entry = tk.Entry(format,width=5)
test_size_entry.insert(0, '20')
test_size_entry.pack(side=tk.LEFT)
tk.Button(format, text="开始训练", command=ml).pack(side=tk.LEFT)
tk.Button(format, text="测试", command=test).pack(side=tk.LEFT)

# 创建画布
canvas = tk.Canvas(root, width=431, height=431, bg='white')
canvas.pack()

draw_line()
root.mainloop()